<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Golang Trace Detail with Collapsible Spans</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
  <style>
    :root {
      --primary: #6f42c1;
      --primary-light: #9b7bdb;
      --primary-dark: #4e2a9e;
      --gray: #6c757d;
      --gray-light: #e9ecef;
      --gray-dark: #343a40;
      --white: #ffffff;
      --black: #212529;
      --border-radius: 0.375rem;
      --box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
    }
    
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: 'Inter', sans-serif;
      background-color: #f8f9fa;
      color: var(--black);
      line-height: 1.5;
    }
    
    .container {
      max-width: 1600px;
      margin: 0 auto;
      padding: 1rem;
    }
    
    .card {
      background-color: var(--white);
      border-radius: var(--border-radius);
      box-shadow: var(--box-shadow);
      overflow: hidden;
      margin-bottom: 1.5rem;
    }
    
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 1rem 1.5rem;
      border-bottom: 1px solid var(--gray-light);
    }
    
    .card-title {
      font-size: 1.25rem;
      font-weight: 600;
      color: var(--primary-dark);
      display: flex;
      align-items: center;
      gap: 0.75rem;
    }
    
    .card-actions {
      display: flex;
      gap: 0.75rem;
    }
    
    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 0.5rem;
      padding: 0.5rem 1rem;
      border-radius: var(--border-radius);
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s ease;
      border: 1px solid transparent;
    }
    
    .btn-primary {
      background-color: var(--primary);
      color: var(--white);
    }
    
    .btn-primary:hover {
      background-color: var(--primary-dark);
    }
    
    .btn-outline {
      background-color: transparent;
      color: var(--primary);
      border-color: var(--primary);
    }
    
    .btn-outline:hover {
      background-color: var(--primary);
      color: var(--white);
    }
    
    .card-body {
      padding: 1.5rem;
    }
    
    /* Trace概览信息 */
    .trace-overview {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 1.5rem;
      margin-bottom: 1.5rem;
      padding-bottom: 1.5rem;
      border-bottom: 1px solid var(--gray-light);
    }
    
    .trace-info-item {
      display: flex;
      flex-direction: column;
    }
    
    .trace-info-label {
      font-size: 0.8rem;
      color: var(--gray);
      margin-bottom: 0.25rem;
      font-weight: 500;
    }
    
    .trace-info-value {
      font-size: 1rem;
      font-weight: 600;
    }
    
    .trace-id {
      font-family: monospace;
      color: var(--primary);
    }
    
    /* 折叠表格样式 */
    .span-table {
      width: 100%;
      border-collapse: collapse;
      font-size: 0.9rem;
    }
    
    .span-table th {
      text-align: left;
      padding: 0.75rem 1rem;
      background-color: var(--gray-light);
      font-weight: 600;
      color: var(--gray-dark);
      position: sticky;
      top: 0;
    }
    
    .span-table td {
      padding: 0.75rem 1rem;
      border-bottom: 1px solid var(--gray-light);
      vertical-align: top;
    }
    
    .span-row {
      cursor: pointer;
    }
    
    .span-row:hover {
      background-color: rgba(111, 66, 193, 0.05);
    }
    
    .span-row.has-children > td:first-child {
      position: relative;
      padding-left: 2rem;
    }
    
    .toggle-icon {
      position: absolute;
      left: 1rem;
      top: 1.25rem;
      color: var(--primary);
      transition: transform 0.2s ease;
    }
    
    .span-row.expanded .toggle-icon {
      transform: rotate(90deg);
    }
    
    .span-id {
      font-family: monospace;
      color: var(--primary);
    }
    
    .span-name {
      font-weight: 500;
    }
    
    .span-duration {
      font-weight: 500;
      white-space: nowrap;
    }
    
    .span-timestamp {
      color: var(--gray);
      font-size: 0.8rem;
      white-space: nowrap;
    }
    
    /* 嵌套span样式 */
    .nested-spans {
      display: none;
    }
    
    .span-row.expanded + .nested-spans {
      display: table-row;
    }
    
    .nested-spans td {
      padding-left: 2rem;
      background-color: rgba(0, 0, 0, 0.02);
    }
    
    /* Arguments/Returns折叠内容 */
    .collapsible-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease;
      background-color: rgba(0, 0, 0, 0.02);
      border-radius: var(--border-radius);
      margin-top: 0.5rem;
    }
    
    .collapsible-content.expanded {
      max-height: 300px;
      overflow: auto;
    }
    
    .json-viewer {
      font-family: monospace;
      font-size: 0.8rem;
      padding: 0.5rem;
      background-color: var(--white);
      border-radius: var(--border-radius);
      border: 1px solid var(--gray-light);
      max-height: 200px;
      overflow: auto;
    }
    
    .toggle-content-btn {
      background: none;
      border: none;
      color: var(--primary);
      cursor: pointer;
      font-size: 0.8rem;
      padding: 0.25rem 0;
      display: inline-flex;
      align-items: center;
      gap: 0.25rem;
    }
    
    .toggle-content-btn i {
      transition: transform 0.2s ease;
    }
    
    .toggle-content-btn.expanded i {
      transform: rotate(90deg);
    }
    
    /* 响应式调整 */
    @media (max-width: 1200px) {
      .span-table {
        display: block;
        overflow-x: auto;
      }
    }
    
    @media (max-width: 768px) {
      .trace-overview {
        grid-template-columns: 1fr;
      }
      
      .card-actions {
        flex-direction: column;
        width: 100%;
      }
      
      .btn {
        width: 100%;
      }
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="card">
      <div class="card-header">
        <h2 class="card-title">
          <i class="fas fa-project-diagram"></i> Trace Detail - 6a057f1d
        </h2>
        <div class="card-actions">
          <button class="btn btn-outline">
            <i class="fas fa-download"></i> Export
          </button>
          <button class="btn btn-primary">
            <i class="fas fa-cog"></i> Settings
          </button>
        </div>
      </div>
      <div class="card-body">
        <!-- Trace概览信息 -->
        <div class="trace-overview">
          <div class="trace-info-item">
            <span class="trace-info-label">Trace ID</span>
            <span class="trace-info-value trace-id">6a057f1d</span>
          </div>
          <div class="trace-info-item">
            <span class="trace-info-label">Root Span</span>
            <span class="trace-info-value">BufferedHttpTransaction</span>
          </div>
          <div class="trace-info-item">
            <span class="trace-info-label">Total Duration</span>
            <span class="trace-info-value">142 ms</span>
          </div>
          <div class="trace-info-item">
            <span class="trace-info-label">Total Spans</span>
            <span class="trace-info-value">48</span>
          </div>
          <div class="trace-info-item">
            <span class="trace-info-label">Start Time</span>
            <span class="trace-info-value">2023-11-15 14:32:45</span>
          </div>
        </div>
        
        <!-- 折叠表格 -->
        <table class="span-table">
          <thead>
            <tr>
              <th style="width: 40px;"></th>
              <th>Service</th>
              <th>Region</th>
              <th>Function</th>
              <th>Position</th>
              <th>Span</th>
              <th>Duration</th>
              <th>Arguments/Returns</th>
            </tr>
          </thead>
          <tbody id="spanTableBody">
            <!-- 数据将通过JavaScript动态填充 -->
          </tbody>
        </table>
      </div>
    </div>
  </div>

  <script>
    // 示例数据 - 模拟多层嵌套的span结构
    const traceDetail = {
      traceId: "6a057f1d",
      rootSpan: "BufferedHttpTransaction",
      totalDuration: "142 ms",
      totalSpans: 48,
      startTime: "2023-11-15 14:32:45",
      spans: [
        {
          id: "afa5f975",
          service: "api-gateway",
          region: "us-east-1",
          function: "handleRequest",
          position: "1",
          name: "http.client-xhr GET",
          description: "https://365.kdocs.cn/woa/api/v1/emojis?offset=0&limit=29",
          duration: "1.80ms",
          timestamp: "1d ago",
          arguments: {
            method: "GET",
            url: "https://365.kdocs.cn/woa/api/v1/emojis",
            params: {
              offset: 0,
              limit: 29
            },
            headers: {
              "Content-Type": "application/json",
              "Authorization": "Bearer xyz123"
            }
          },
          returns: {
            status: 200,
            data: {
              emojis: ["😀", "😊", "👍"]
            }
          },
          children: [
            {
              id: "bb4d863f",
              service: "user-service",
              region: "us-east-1",
              function: "getUserData",
              position: "1.1",
              name: "http.client-xhr GET",
              description: "https://365.kdocs.cn/woa/api/v1/mine/chat/todo/count",
              duration: "10.13min",
              timestamp: "1d ago",
              arguments: {
                method: "GET",
                url: "https://365.kdocs.cn/woa/api/v1/mine/chat/todo/count",
                params: {
                  chat_ids: "417432",
                  state: 0
                }
              },
              returns: {
                status: 200,
                data: {
                  count: 5
                }
              },
              children: [
                {
                  id: "bee02683",
                  service: "user-service",
                  region: "us-east-1",
                  function: "processData",
                  position: "1.1.1",
                  name: "http.client-xhr GET",
                  description: "https://365.kdocs.cn/woa/api/v1/emojis",
                  duration: "6.01min",
                  timestamp: "24h ago",
                  arguments: {
                    method: "GET",
                    url: "https://365.kdocs.cn/woa/api/v1/emojis",
                    params: {
                      offset: 0,
                      limit: 29
                    }
                  },
                  returns: {
                    status: 200,
                    data: {
                      emojis: ["😀", "😊", "👍"]
                    }
                  }
                },
                {
                  id: "a3bc0212",
                  service: "auth-service",
                  region: "us-east-1",
                  function: "checkAuth",
                  position: "1.1.2",
                  name: "http.client-xhr GET",
                  description: "https://365.kdocs.cn/woa/api/v1/online_status",
                  duration: "5.97min",
                  timestamp: "24h ago",
                  arguments: {
                    method: "GET",
                    url: "https://365.kdocs.cn/woa/api/v1/online_status",
                    params: {
                      userids: "333621472"
                    }
                  },
                  returns: {
                    status: 200,
                    data: {
                      online: true,
                      lastActive: "2023-11-15T14:30:00Z"
                    }
                  },
                  children: [
                    {
                      id: "8874f19d",
                      service: "db-service",
                      region: "us-east-1",
                      function: "queryDB",
                      position: "1.1.2.1",
                      name: "db.query",
                      description: "SELECT * FROM users WHERE id = ?",
                      duration: "5.83min",
                      timestamp: "24h ago",
                      arguments: {
                        query: "SELECT * FROM users WHERE id = ?",
                        params: ["333621472"]
                      },
                      returns: {
                        rows: [
                          {
                            id: "333621472",
                            name: "John Doe",
                            email: "john@example.com"
                          }
                        ]
                      }
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          id: "a59cd728",
          service: "api-gateway",
          region: "us-east-1",
          function: "handleRequest",
          position: "2",
          name: "http.client-xhr GET",
          description: "https://365.kdocs.cn/woa/api/v2/contacts",
          duration: "5.82min",
          timestamp: "24h ago",
          arguments: {
            method: "GET",
            url: "https://365.kdocs.cn/woa/api/v2/contacts",
            params: {
              userids: "333621472",
              use: "new_status_enum=true"
            }
          },
          returns: {
            status: 200,
            data: {
              contacts: [
                {
                  id: "123",
                  name: "Alice"
                },
                {
                  id: "456",
                  name: "Bob"
                }
              ]
            }
          },
          children: [
            {
              id: "b2c39ab6",
              service: "user-service",
              region: "us-east-1",
              function: "getContacts",
              position: "2.1",
              name: "http.client-xhr GET",
              description: "https://365.kdocs.cn/woa/api/v2/meeting",
              duration: "5.79min",
              timestamp: "24h ago",
              arguments: {
                method: "GET",
                url: "https://365.kdocs.cn/woa/api/v2/meeting"
              },
              returns: {
                status: 200,
                data: {
                  meetings: []
                }
              }
            },
            {
              id: "bb572e83",
              service: "user-service",
              region: "us-east-1",
              function: "processContacts",
              position: "2.2",
              name: "http.client-xhr GET",
              description: "https://365.kdocs.cn/woa/api/v2/meeting",
              duration: "5.61min",
              timestamp: "24h ago",
              arguments: {
                method: "GET",
                url: "https://365.kdocs.cn/woa/api/v2/meeting"
              },
              returns: {
                status: 200,
                data: {
                  meetings: []
                }
              }
            }
          ]
        },
        {
          id: "1eb72eb8",
          service: "api-gateway",
          region: "us-east-1",
          function: "finalizeResponse",
          position: "3",
          name: "GotBufferedHttpTransaction",
          description: "Finalize HTTP response",
          duration: "5.55min",
          timestamp: "24h ago",
          arguments: {
            statusCode: 200,
            headers: {
              "Content-Type": "application/json"
            }
          },
          returns: {
            success: true
          }
        }
      ]
    };

    // 渲染span表格
    function renderSpanTable() {
      const tableBody = document.getElementById('spanTableBody');
      tableBody.innerHTML = '';
      
      // 递归渲染span及其子span
      function renderSpans(spans, level = 0) {
        spans.forEach(span => {
          // 主行
          const row = document.createElement('tr');
          row.className = 'span-row' + (span.children ? ' has-children' : '');
          
          // 创建toggle按钮
          const toggleCell = document.createElement('td');
          if (span.children) {
            const toggleIcon = document.createElement('i');
            toggleIcon.className = 'fas fa-chevron-right toggle-icon';
            toggleCell.appendChild(toggleIcon);
          }
          
          // 创建其他单元格
          const serviceCell = document.createElement('td');
          serviceCell.textContent = span.service;
          
          const regionCell = document.createElement('td');
          regionCell.textContent = span.region;
          
          const functionCell = document.createElement('td');
          functionCell.textContent = span.function;
          
          const positionCell = document.createElement('td');
          positionCell.textContent = span.position;
          
          const spanCell = document.createElement('td');
          spanCell.innerHTML = `<span class="span-name">${span.name}</span><br><small>${span.description}</small>`;
          
          const durationCell = document.createElement('td');
          durationCell.innerHTML = `<span class="span-duration">${span.duration}</span><br><small class="span-timestamp">${span.timestamp}</small>`;
          
          const argsReturnsCell = document.createElement('td');
          
          // Arguments/Returns折叠内容
          const argsBtn = document.createElement('button');
          argsBtn.className = 'toggle-content-btn';
          argsBtn.innerHTML = '<i class="fas fa-chevron-right"></i> Arguments';
          argsBtn.onclick = (e) => {
            e.stopPropagation();
            argsBtn.classList.toggle('expanded');
            const argsContent = argsBtn.nextElementSibling;
            argsContent.classList.toggle('expanded');
          };
          
          const argsContent = document.createElement('div');
          argsContent.className = 'collapsible-content';
          const argsPre = document.createElement('pre');
          argsPre.className = 'json-viewer';
          argsPre.textContent = JSON.stringify(span.arguments, null, 2);
          argsContent.appendChild(argsPre);
          
          const returnsBtn = document.createElement('button');
          returnsBtn.className = 'toggle-content-btn';
          returnsBtn.innerHTML = '<i class="fas fa-chevron-right"></i> Returns';
          returnsBtn.onclick = (e) => {
            e.stopPropagation();
            returnsBtn.classList.toggle('expanded');
            const returnsContent = returnsBtn.nextElementSibling;
            returnsContent.classList.toggle('expanded');
          };
          
          const returnsContent = document.createElement('div');
          returnsContent.className = 'collapsible-content';
          const returnsPre = document.createElement('pre');
          returnsPre.className = 'json-viewer';
          returnsPre.textContent = JSON.stringify(span.returns, null, 2);
          returnsContent.appendChild(returnsPre);
          
          argsReturnsCell.appendChild(argsBtn);
          argsReturnsCell.appendChild(argsContent);
          argsReturnsCell.appendChild(document.createElement('br'));
          argsReturnsCell.appendChild(returnsBtn);
          argsReturnsCell.appendChild(returnsContent);
          
          // 组装行
          row.appendChild(toggleCell);
          row.appendChild(serviceCell);
          row.appendChild(regionCell);
          row.appendChild(functionCell);
          row.appendChild(positionCell);
          row.appendChild(spanCell);
          row.appendChild(durationCell);
          row.appendChild(argsReturnsCell);
          
          tableBody.appendChild(row);
          
          // 如果有子span，添加嵌套行
          if (span.children) {
            const nestedRow = document.createElement('tr');
            nestedRow.className = 'nested-spans';
            const nestedCell = document.createElement('td');
            nestedCell.colSpan = 8;
            
            // 创建嵌套表格
            const nestedTable = document.createElement('table');
            nestedTable.className = 'span-table';
            nestedTable.style.width = '100%';
            nestedTable.style.border = 'none';
            
            const nestedTbody = document.createElement('tbody');
            renderSpans(span.children, level + 1);
            nestedTable.appendChild(nestedTbody);
            nestedCell.appendChild(nestedTable);
            nestedRow.appendChild(nestedCell);
            
            tableBody.appendChild(nestedRow);
            
            // 添加点击事件
            row.addEventListener('click', (e) => {
              if (!e.target.classList.contains('toggle-content-btn')) {
                row.classList.toggle('expanded');
                const nestedRow = row.nextElementSibling;
                nestedRow.style.display = row.classList.contains('expanded') ? 'table-row' : 'none';
              }
            });
          }
        });
      }
      
      renderSpans(traceDetail.spans);
    }

    // 页面加载后初始化
    window.addEventListener('load', () => {
      renderSpanTable();
    });
  </script>
</body>
</html>